﻿using OSGeo.GDAL;
using OSGeo.OGR;
using OSGeo.OSR;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OSGeo.GDAL;
using OSGeo.OSR;
using Newtonsoft.Json;

namespace ConsoleApp
{
    public class GeoTiffOverlapProcessor
    {
        private const string TILE_NAME_FORMAT = "geotiff_{0}_{1}_{2}.png";
        private const string METADATA_FILE = "tile_metadata.json";

        public static void ProcessOverlappedGeoTiffs(
            string[] inputPaths, string outputDir,
            int tileSize = 512, bool overwrite = false)
        {
            // 1. 验证输入
            ValidateInputs(inputPaths, outputDir);

            // 2. 初始化GDAL
            //必须的，不然会报错 “OSGeo.GDAL.GdalPINVOKE”的类型初始值设定项引发异常
            GdalConfiguration.ConfigureGdal();
            GdalConfiguration.ConfigureOgr();//矢量

            // 3. 计算公共重叠区域
            CustomEnvelope commonEnvelope = CalculateCommonEnvelope(inputPaths);
            if (commonEnvelope == null)
                throw new InvalidOperationException("输入文件没有重叠区域");

            // 4. 创建统一网格
            TileGrid unifiedGrid = CreateUnifiedGrid(inputPaths, commonEnvelope, tileSize);

            // 5. 处理每个文件
            var metadata = new Dictionary<string, List<GeoTileInfo>>();
            for (int i = 0; i < inputPaths.Length; i++)
            {
                string typeId = GetTypeIdentifier(inputPaths[i]);
                var fileMetadata = ProcessSingleFile(
                    inputPaths[i], outputDir, unifiedGrid,
                    typeId, tileSize, overwrite);

                metadata[typeId] = fileMetadata;
            }

            // 6. 保存全局元数据
            SaveGlobalMetadata(outputDir, metadata, unifiedGrid);
        }

        private static void ValidateInputs(string[] inputPaths, string outputDir)
        {
            if (inputPaths == null || inputPaths.Length < 2)
                throw new ArgumentException("至少需要两个输入文件");

            foreach (var path in inputPaths)
            {
                if (!File.Exists(path))
                    throw new FileNotFoundException("输入文件不存在", path);
            }

            Directory.CreateDirectory(outputDir);
        }

        private static CustomEnvelope CalculateCommonEnvelope(string[] paths)
        {
            CustomEnvelope commonEnv = null;

            foreach (var path in paths)
            {
                using (Dataset ds = Gdal.Open(path, Access.GA_ReadOnly))
                {
                    if (ds == null) continue;

                    double[] gt = new double[6];
                    ds.GetGeoTransform(gt);

                    // 计算当前文件的边界
                    double minX = gt[0];
                    double maxY = gt[3];
                    double maxX = minX + ds.RasterXSize * gt[1];
                    double minY = maxY + ds.RasterYSize * gt[5];

                    // 创建当前文件的地理范围
                    var env = new CustomEnvelope(minX, maxX, minY, maxY);

                    // 计算交集
                    commonEnv = (commonEnv == null) ? env : commonEnv.Intersection(env);
                }
            }
            return commonEnv;
        }

        private static string GetTypeIdentifier(string filePath)
        {
            string fileName = Path.GetFileNameWithoutExtension(filePath);

            // 默认类型标识：使用文件后缀的最后部分
            if (fileName.EndsWith("_T", StringComparison.OrdinalIgnoreCase)) return "T";    
            if (fileName.EndsWith("_V", StringComparison.OrdinalIgnoreCase)) return "V";

            // 没有特定标识时使用文件索引
            return Path.GetExtension(filePath).TrimStart('.');
        }

        private static TileGrid CreateUnifiedGrid(
            string[] inputPaths, CustomEnvelope envelope, int tileSize)
        {
            // 确定最高分辨率（最小像素大小）
            double minPixelSize = double.MaxValue;

            foreach (var path in inputPaths)
            {
                using (Dataset ds = Gdal.Open(path, Access.GA_ReadOnly))
                {
                    if (ds == null) continue;

                    double[] gt = new double[6];
                    ds.GetGeoTransform(gt);

                    // 取X方向像素大小的绝对值
                    double pixelSize = Math.Abs(gt[1]);
                    if (pixelSize < minPixelSize) minPixelSize = pixelSize;
                }
            }

            if (minPixelSize == double.MaxValue)
                throw new InvalidOperationException("无法确定分辨率");

            // 计算网格参数
            double widthGeo = envelope.MaxX - envelope.MinX;
            double heightGeo = envelope.MaxY - envelope.MinY;

            int cols = (int)Math.Ceiling(widthGeo / (minPixelSize * tileSize));
            int rows = (int)Math.Ceiling(heightGeo / (minPixelSize * tileSize));

            return new TileGrid(
                rows, cols,
                envelope.MinX, envelope.MaxY,
                minPixelSize, minPixelSize
            );
        }

        private static List<GeoTileInfo> ProcessSingleFile(
            string inputPath, string outputDir,
            TileGrid unifiedGrid, string typeId,
            int tileSize, bool overwrite)
        {
            // 创建类型特定输出目录
            string typeDir = Path.Combine(outputDir, typeId);
            Directory.CreateDirectory(typeDir);

            using (Dataset dataset = Gdal.Open(inputPath, Access.GA_ReadOnly))
            {
                if (dataset == null)
                    throw new InvalidOperationException($"无法打开文件: {inputPath}");

                double[] geoTransform = new double[6];
                dataset.GetGeoTransform(geoTransform);

                // 获取颜色表（如果存在）
                ColorTable colorTable = null;
                using (Band firstBand = dataset.GetRasterBand(1))
                {
                    colorTable = firstBand.GetRasterColorTable()?.Clone();
                }

                List<GeoTileInfo> tiles = new List<GeoTileInfo>();

                // 按统一网格处理切片
                for (int row = 0; row < unifiedGrid.Rows; row++)
                {
                    for (int col = 0; col < unifiedGrid.Cols; col++)
                    {
                        var tileInfo = ProcessTile(
                            dataset, row, col,
                            unifiedGrid, geoTransform,
                            typeDir, typeId,
                            tileSize, overwrite,
                            colorTable);

                        if (tileInfo != null) tiles.Add(tileInfo);
                    }
                }

                // 保存文件级元数据
                File.WriteAllText(
                    Path.Combine(typeDir, METADATA_FILE),
                    JsonConvert.SerializeObject(tiles, Formatting.Indented)
                );

                return tiles;
            }
        }

        private static GeoTileInfo ProcessTile(
            Dataset dataset, int row, int col,
            TileGrid grid, double[] geoTransform,
            string outputDir, string typeId,
            int tileSize, bool overwrite,
            ColorTable colorTable)
        {
            // 计算当前切片的实际地理范围
            double tileMinX = grid.OriginX + col * tileSize * grid.PixelSizeX;
            double tileMaxY = grid.OriginY - row * tileSize * grid.PixelSizeY;
            double tileMaxX = tileMinX + tileSize * grid.PixelSizeX;
            double tileMinY = tileMaxY - tileSize * grid.PixelSizeY;

            // 转换到当前文件的像素坐标
            var (pxStart, pyStart) = GeoToPixel(tileMinX, tileMaxY, geoTransform);
            var (pxEnd, pyEnd) = GeoToPixel(tileMaxX, tileMinY, geoTransform);

            // 计算切片尺寸
            int width = pxEnd - pxStart;
            int height = pyEnd - pyStart;

            // 跳过无效或越界切片
            if (width <= 0 || height <= 0 ||
                pxStart < 0 || pyStart < 0 ||
                pxEnd > dataset.RasterXSize || pyEnd > dataset.RasterYSize)
            {
                return null;
            }

            // 生成统一命名的文件
            string fileName = string.Format(TILE_NAME_FORMAT, row, col, typeId);
            string filePath = Path.Combine(outputDir, fileName);

            // 跳过已存在文件
            if (!overwrite && File.Exists(filePath))
            {
                Console.WriteLine($"跳过 {fileName}");
                return CreateTileInfo(fileName, row, col, typeId, tileMinX, tileMaxY, tileMaxX, tileMinY);
            }

            // 读取切片数据
            byte[][] tileData = ReadTileData(dataset, pxStart, pyStart, width, height);

            // 跳过空白切片
            if (GeoTiffSplitter.IsTileBlank(tileData))
            {
                Console.WriteLine($"跳过空白切片 {fileName}");
                return null;
            }

            // 保存切片
            SaveTile(tileData, width, height, filePath, colorTable);

            return CreateTileInfo(fileName, row, col, typeId, tileMinX, tileMaxY, tileMaxX, tileMinY);
        }

        private static (int x, int y) GeoToPixel(double geoX, double geoY, double[] transform)
        {
            // 使用仿射变换公式
            double x = (geoX - transform[0]) / transform[1];
            double y = (geoY - transform[3]) / transform[5];
            return ((int)Math.Round(x), (int)Math.Round(y));
        }

        private static byte[][] ReadTileData(Dataset dataset, int xStart, int yStart, int width, int height)
        {
            int bandCount = dataset.RasterCount;
            byte[][] tileData = new byte[bandCount][];

            for (int b = 0; b < bandCount; b++)
            {
                using (Band band = dataset.GetRasterBand(b + 1))
                {
                    tileData[b] = new byte[width * height];
                    band.ReadRaster(xStart, yStart, width, height,
                                  tileData[b], width, height, 0, 0);
                }
            }
            return tileData;
        }

        private static void SaveTile(byte[][] tileData, int width, int height, string path, ColorTable colorTable)
        {
            int bandCount = tileData.Length;

            // 颜色处理决策
            if (bandCount == 1 && colorTable != null)
            {
                SaveWithColorTable(tileData[0], colorTable, width, height, path);
            }
            else if (bandCount >= 3)
            {
                SaveAsTrueColor(tileData[0], tileData[1], tileData[2], width, height, path);
            }
            else
            {
                GeoTiffSplitter.GeneratePseudocolor(tileData[0], width, height, path);
            }
        }

        private static GeoTileInfo CreateTileInfo(
            string fileName, int row, int col, string typeId,
            double minX, double maxY, double maxX, double minY)
        {
            return new GeoTileInfo
            {
                FileName = fileName,
                Row = row,
                Col = col,
                TypeId = typeId,
                TopLeft = new GeoPoint { Lon = minX, Lat = maxY },
                BottomRight = new GeoPoint { Lon = maxX, Lat = minY },
                Center = new GeoPoint
                {
                    Lon = (minX + maxX) / 2,
                    Lat = (minY + maxY) / 2
                }
            };
        }

        private static void SaveGlobalMetadata(
            string outputDir,
            Dictionary<string, List<GeoTileInfo>> metadata,
            TileGrid grid)
        {
            var globalMetadata = new
            {
                Grid = new
                {
                    Rows = grid.Rows,
                    Columns = grid.Cols,
                    OriginX = grid.OriginX,
                    OriginY = grid.OriginY,
                    PixelSizeX = grid.PixelSizeX,
                    PixelSizeY = grid.PixelSizeY
                },
                Files = metadata.Keys.ToDictionary(
                    k => k,
                    k => metadata[k].Count)
            };

            File.WriteAllText(
                Path.Combine(outputDir, "global_metadata.json"),
                JsonConvert.SerializeObject(globalMetadata, Formatting.Indented)
            );
        }

        // 以下是复用原GeoTiffSplitter中的图像处理函数
        private static void SaveWithColorTable(byte[] data, ColorTable ct,
                                             int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
            {
                // 设置调色板
                ColorPalette palette = bmp.Palette;
                for (int i = 0; i < ct.GetCount(); i++)
                {
                    ColorEntry ce = ct.GetColorEntry(i);
                    palette.Entries[i] = Color.FromArgb(ce.c4, ce.c1, ce.c2, ce.c3);
                }
                bmp.Palette = palette;

                // 快速写入数据
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                              ImageLockMode.WriteOnly,
                                              PixelFormat.Format8bppIndexed);
                Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        private static void SaveAsTrueColor(byte[] r, byte[] g, byte[] b,
                                          int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
            {
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.WriteOnly,
                                                PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0;
                    for (int i = 0; i < width * height; i++)
                    {
                        ptr[2] = r[i]; // R
                        ptr[1] = g[i]; // G
                        ptr[0] = b[i]; // B
                        ptr += 3;
                    }
                }
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }


        
    }

    public class CustomEnvelope
    {
        public double MinX { get; }
        public double MaxX { get; }
        public double MinY { get; }
        public double MaxY { get; }

        public CustomEnvelope(double minX, double maxX, double minY, double maxY)
        {
            MinX = minX;
            MaxX = maxX;
            MinY = minY;
            MaxY = maxY;
        }

        public CustomEnvelope Intersection(CustomEnvelope other)
        {
            double newMinX = Math.Max(MinX, other.MinX);
            double newMaxX = Math.Min(MaxX, other.MaxX);
            double newMinY = Math.Max(MinY, other.MinY);
            double newMaxY = Math.Min(MaxY, other.MaxY);

            if (newMinX > newMaxX || newMinY > newMaxY)
                return null; // 无重叠

            return new CustomEnvelope(newMinX, newMaxX, newMinY, newMaxY);
        }
    }

    public class TileGrid
    {
        public int Rows { get; }
        public int Cols { get; }
        public double OriginX { get; }
        public double OriginY { get; }
        public double PixelSizeX { get; }
        public double PixelSizeY { get; }

        public TileGrid(int rows, int cols, double originX, double originY,
                       double pixelSizeX, double pixelSizeY)
        {
            Rows = rows;
            Cols = cols;
            OriginX = originX;
            OriginY = originY;
            PixelSizeX = pixelSizeX;
            PixelSizeY = pixelSizeY;
        }
    }


}
